package com.ashokvarma.bottomnavigation;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;

public class BottomNavigationBar extends Component implements Component.DrawTask, Component.TouchEventListener {
    private static final int DEFAULT_ANIMATION_DURATION = 200;
    private static final int MIN_SIZE = 3;
    private static final int MAX_SIZE = 5;

    public static final int MODE_DEFAULT = 0;
    public static final int MODE_FIXED = 1;
    public static final int MODE_SHIFTING = 2;
    public static final int MODE_FIXED_NO_TITLE = 3;
    public static final int MODE_SHIFTING_NO_TITLE = 4;

    public static final int BACKGROUND_STYLE_DEFAULT = 0;
    public static final int BACKGROUND_STYLE_STATIC = 1;
    public static final int BACKGROUND_STYLE_RIPPLE = 2;

    private int fixedTitleSizeActive = dp2px(14);
    private int fixedTitleSizeInActive = dp2px(12);

    private int shiftingTitleSizeActive = dp2px(14);
    private int shiftingTitleSizeInActive = dp2px(0);

    private int fixedTopActive = dp2px(0);
    private int fixedTopInActive = dp2px(2);

    private int shiftingTopActive = dp2px(0);
    private int shiftingTopInActive = dp2px(5);

    private int textCenterY = dp2px(39);

    private int iconSize = dp2px(24);
    private int noTitleIconSize = dp2px(32);

    private int height = dp2px(56);

    private OnTabSelectedListener mTabSelectedListener;

    boolean isFirst = true;
    boolean needInit = true;
    boolean mIsHidden = true;

    private AnimatorValue showAnimatorValue;
    private int showBottom = 0;
    private long showDurationTime = 300;

    private int selectWidth;
    private int unSelectWidth;
    private int offsetX;

    private int mActiveColor;
    private int mInActiveColor;
    private int mBackgroundColor;
    private int mElevation;

    private int mAnimationDuration = DEFAULT_ANIMATION_DURATION;
    private int mRippleAnimationDuration = (int) (DEFAULT_ANIMATION_DURATION * 2.5);

    private int mMode = MODE_DEFAULT;
    private int mBackgroundStyle = BACKGROUND_STYLE_DEFAULT;

    private Paint mPaint = new Paint();
    private Paint elevationPaint = new Paint();
    private Paint mOverPaint = new Paint();
    private Paint mTextPaint = new Paint();
    private boolean mIsDragging;

    private int lastPosition;
    private int mPosition;

    private AnimatorValue changeAnimatorValue;
    private AnimatorValue overAnimatorValue;
    private float changePose = 1;
    private float overPose = 1;

    private boolean touchEffect;

    private ArrayList<BottomNavigationItem> mBottomNavigationItems = new ArrayList<>();

    public BottomNavigationBar(Context context) {
        this(context, null, null);
    }

    public BottomNavigationBar(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public BottomNavigationBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);

        mActiveColor = AttrUtils.getColorFromAttr(attrSet, "bnbActiveColor", Color.WHITE.getValue());
        mInActiveColor = AttrUtils.getColorFromAttr(attrSet, "bnbInactiveColor", Color.LTGRAY.getValue());
        mBackgroundColor = AttrUtils.getColorFromAttr(attrSet, "bnbBackgroundColor", Color.WHITE.getValue());
        mElevation = AttrUtils.getDimensionFromAttr(attrSet, "bnbElevation", dp2px(8));
        mAnimationDuration = AttrUtils.getIntFromAttr(attrSet, "bnbAnimationDuration", DEFAULT_ANIMATION_DURATION);
        mMode = AttrUtils.getIntFromAttr(attrSet, "bnbMode", MODE_DEFAULT);
        mBackgroundStyle = AttrUtils.getColorFromAttr(attrSet, "bnbBackgroundStyle", BACKGROUND_STYLE_DEFAULT);
        touchEffect = AttrUtils.getBooleanFromAttr(attrSet, "bnbTouchEffect", false);

        init();
    }

    private int downPostion = -1;

    private void init() {
        mPaint.setAntiAlias(true);
        mOverPaint.setAntiAlias(true);
        mTextPaint.setAntiAlias(true);
        setTouchEventListener(this);
        addDrawTask(this);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            float touchX = getTouchX(touchEvent, 0);
            float touchY = getTouchY(touchEvent, 0);
            if (mBottomNavigationItems != null && mBottomNavigationItems.size() > 0) {
                downPostion = getPostionFromX(touchX);
                if (touchEffect) {
                    if (overAnimatorValue == null || !overAnimatorValue.isRunning()) {
                        invalidate();
                    }
                }
            }
        } else if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            if (downPostion != -1) {
                float touchX = getTouchX(touchEvent, 0);
                float touchY = getTouchY(touchEvent, 0);
                if (getPostionFromX(touchX) == downPostion && touchY >= 0 && touchY <= height) {
                    if (mBottomNavigationItems != null && mBottomNavigationItems.size() > downPostion) {
                        selectTab(downPostion);
                        downPostion = -1;
                        if (touchEffect) {
                            if (overAnimatorValue == null || !overAnimatorValue.isRunning()) {
                                invalidate();
                            }
                        }
                    }
                }
            }
        } else if (touchEvent.getAction() == TouchEvent.POINT_MOVE) {
            if (downPostion != -1) {
                float touchX = getTouchX(touchEvent, 0);
                float touchY = getTouchY(touchEvent, 0);
                if (getPostionFromX(touchX) != downPostion || touchY < 0 || touchY > height) {
                    downPostion = -1;
                    if (mBottomNavigationItems != null && mBottomNavigationItems.size() > 0) {
                        if (touchEffect) {
                            if (overAnimatorValue == null || !overAnimatorValue.isRunning()) {
                                invalidate();
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (isFirst) {
            isFirst = false;
            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
            layoutConfig.setMarginBottom(0);
            layoutConfig.height = height + mElevation;
            setLayoutConfig(layoutConfig);
        }
        if (mBottomNavigationItems != null && mBottomNavigationItems.size() > 0) {
            drawBackground(canvas);
            drawItems(canvas);
        }
    }

    private void drawBackground(Canvas canvas) {
        if (needInit) {
            needInit = false;
            if (mMode == MODE_SHIFTING || mMode == MODE_SHIFTING_NO_TITLE) {
                getMeasurementsForShiftingMode(getContext(), getWidth(), mBottomNavigationItems.size(), false);
            } else {
                getMeasurementsForFixedMode(getContext(), getWidth(), mBottomNavigationItems.size(), false);
            }
        }

        LinearShader gradient =
                new LinearShader(
                        new Point[] {new Point(0, 0), new Point(0, mElevation)},
                        new float[] {0f, 0.3f, 1f},
                        new Color[] {new Color(0x00000000), new Color(0x00000000), new Color(0x10000000)},
                        Shader.TileMode.CLAMP_TILEMODE);

        elevationPaint.setShader(gradient, Paint.ShaderType.LINEAR_SHADER);
        canvas.drawRect(new RectFloat(0, 0, getWidth(), mElevation), elevationPaint);

        if (mBackgroundStyle == BACKGROUND_STYLE_RIPPLE) {
            if (mPosition >= 0 && mPosition < mBottomNavigationItems.size()) {
                if (overPose == 1) {
                    mPaint.setColor(new Color(mBottomNavigationItems.get(mPosition).getActiveColor()));
                } else if (lastPosition >= 0 && lastPosition < mBottomNavigationItems.size()) {
                    mOverPaint.setColor(new Color(mBottomNavigationItems.get(mPosition).getActiveColor()));
                    mPaint.setColor(new Color(mBottomNavigationItems.get(lastPosition).getActiveColor()));
                }
            } else {
                mPaint.setColor(new Color(mActiveColor));
            }
        } else {
            mPaint.setColor(new Color(mBackgroundColor));
        }

        canvas.drawRect(new RectFloat(0, mElevation, getWidth(), mElevation + height), mPaint);

        if (mBackgroundStyle == BACKGROUND_STYLE_RIPPLE && overPose != 1) {
            canvas.saveLayer(new RectFloat(0, mElevation, getWidth(), height + mElevation), mOverPaint);
            canvas.drawCircle(getCurrentCenterX(mPosition, mPosition), height / 2, getWidth() * overPose, mOverPaint);
            canvas.restore();
        }
    }

    private void drawItems(Canvas canvas) {
        canvas.save();
        canvas.translate(offsetX, mElevation);
        for (int i = 0; i < mBottomNavigationItems.size(); i++) {
            BottomNavigationItem bottomNavigationItem = mBottomNavigationItems.get(i);
            int size = iconSize;
            if (mMode == MODE_FIXED_NO_TITLE || mMode == MODE_SHIFTING_NO_TITLE) {
                size = noTitleIconSize;
            }
            float topOff = 0;
            if (mMode == MODE_SHIFTING || mMode == MODE_SHIFTING_NO_TITLE) {
                if (i != lastPosition && i != mPosition) {
                    topOff = shiftingTopInActive;
                } else if (i == mPosition) {
                    topOff = shiftingTopInActive + (shiftingTopActive - shiftingTopInActive) * changePose;
                } else if (i == lastPosition) {
                    topOff = shiftingTopActive + (shiftingTopInActive - shiftingTopActive) * changePose;
                }
            } else {
                if (i != lastPosition && i != mPosition) {
                    topOff = fixedTopInActive;
                } else if (i == mPosition) {
                    topOff = fixedTopInActive + (fixedTopActive - fixedTopInActive) * changePose;
                } else if (i == lastPosition) {
                    topOff = fixedTopActive + (fixedTopInActive - fixedTopActive) * changePose;
                }
            }

            float centerX =
                    getCurrentCenterX(i, lastPosition)
                            + (getCurrentCenterX(i, mPosition) - getCurrentCenterX(i, lastPosition)) * changePose;
            if (touchEffect && i == downPostion) {
                mPaint.setColor(new Color(0x20000000));
                canvas.drawCircle(centerX, height / 2, unSelectWidth * 0.6f, mPaint);
            }

            PixelMap pixelMap;
            if (mBackgroundStyle == BACKGROUND_STYLE_RIPPLE) {
                if (i == mPosition) {
                    mTextPaint.setColor(
                            new Color(
                                    bottomNavigationItem.getBackgroundColor() != 0
                                            ? bottomNavigationItem.getBackgroundColor()
                                            : getBackgroundColor()));
                    pixelMap = bottomNavigationItem.getBackgroundPixelMap();
                } else {
                    mTextPaint.setColor(
                            new Color(
                                    bottomNavigationItem.getInActiveColor() != 0
                                            ? bottomNavigationItem.getInActiveColor()
                                            : getInActiveColor()));
                    pixelMap = bottomNavigationItem.getInActivePixelMap();
                }
            } else {
                if (i == mPosition) {
                    mTextPaint.setColor(
                            new Color(
                                    bottomNavigationItem.getActiveColor() != 0
                                            ? bottomNavigationItem.getActiveColor()
                                            : getActiveColor()));
                    pixelMap = bottomNavigationItem.getActivePixelMap();
                } else {
                    mTextPaint.setColor(
                            new Color(
                                    bottomNavigationItem.getInActiveColor() != 0
                                            ? bottomNavigationItem.getInActiveColor()
                                            : getInActiveColor()));
                    pixelMap = bottomNavigationItem.getInActivePixelMap();
                }
            }

            if (pixelMap != null) {
                RectFloat rectFloat = new RectFloat();
                rectFloat.left =
                        getCurrentCenterX(i, lastPosition)
                                + (getCurrentCenterX(i, mPosition) - getCurrentCenterX(i, lastPosition)) * changePose
                                - size / 2;
                rectFloat.right =
                        getCurrentCenterX(i, lastPosition)
                                + (getCurrentCenterX(i, mPosition) - getCurrentCenterX(i, lastPosition)) * changePose
                                + size / 2;
                rectFloat.top = dp2px(8) + topOff;
                rectFloat.bottom = dp2px(8) + topOff + size;
                mPaint.setColor(new Color(0xffffffff));
                canvas.drawPixelMapHolderRect(
                        new PixelMapHolder(pixelMap),
                        new RectFloat(0, 0, pixelMap.getImageInfo().size.width, pixelMap.getImageInfo().size.height),
                        rectFloat,
                        mPaint);
            }

            if (mMode == MODE_SHIFTING || mMode == MODE_FIXED) {
                float textSize = 0;
                if (mMode == MODE_SHIFTING || mMode == MODE_SHIFTING_NO_TITLE) {
                    if (i != lastPosition && i != mPosition) {
                        textSize = shiftingTitleSizeInActive;
                    } else if (i == mPosition) {
                        textSize =
                                shiftingTitleSizeInActive
                                        + (shiftingTitleSizeActive - shiftingTitleSizeInActive) * changePose;
                    } else if (i == lastPosition) {
                        textSize =
                                shiftingTitleSizeActive
                                        + (shiftingTitleSizeInActive - shiftingTitleSizeActive) * changePose;
                    }
                } else {
                    if (i != lastPosition && i != mPosition) {
                        textSize = fixedTitleSizeInActive;
                    } else if (i == mPosition) {
                        textSize =
                                fixedTitleSizeInActive + (fixedTitleSizeActive - fixedTitleSizeInActive) * changePose;
                    } else if (i == lastPosition) {
                        textSize = fixedTitleSizeActive + (fixedTitleSizeInActive - fixedTitleSizeActive) * changePose;
                    }
                }
                mTextPaint.setTextSize((int) textSize);
                float textWidth = mTextPaint.measureText(bottomNavigationItem.getTitle());
                float textHeight = mTextPaint.descent() - mTextPaint.ascent();
                canvas.drawText(
                        mTextPaint,
                        bottomNavigationItem.getTitle(),
                        centerX - textWidth / 2,
                        textCenterY + textHeight / 2);
            }

            if (bottomNavigationItem.getBadgeItem() != null && !bottomNavigationItem.getBadgeItem().isHidden()) {
                BadgeItem badgeItem = bottomNavigationItem.getBadgeItem();
                badgeItem.drawToCanvas(
                        canvas, centerX, height / 2 + topOff, size / 2 + dp2px(6), -dp2px(16), dp2px(12), dp2px(1));
            }
        }
        canvas.restore();
    }

    private boolean isDragging() {
        return mIsDragging;
    }

    /**
     * Used to add a new tab.
     *
     * @param item bottom navigation tab details
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar addItem(BottomNavigationItem item) {
        if (mBottomNavigationItems == null) {
            mBottomNavigationItems = new ArrayList<>();
        }
        item.setupPixelMap(this, dp2px(32));
        mBottomNavigationItems.add(item);
        return this;
    }

    /**
     * Used to remove a tab.
     * you should call initialise() after this to see the results effected.
     *
     * @param item bottom navigation tab details
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar removeItem(BottomNavigationItem item) {
        mBottomNavigationItems.remove(item);
        return this;
    }

    /**
     * set mode
     *
     * @param mode any of the three Modes supported by library
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setBarMode(int mode) {
        mMode = mode;
        return this;
    }

    /**
     * set background style
     *
     * @param backgroundStyle any of the three Background Styles supported by library
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setBackgroundStyle(int backgroundStyle) {
        mBackgroundStyle = backgroundStyle;
        return this;
    }

    /**
     * set active color
     *
     * @param activeColor res code for the default active color
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setActiveColor(int activeColor) {
        mActiveColor = activeColor;
        return this;
    }

    /**
     * set in-active color
     *
     * @param inActiveColor res code for the default in-active color
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setInActiveColor(int inActiveColor) {
        mInActiveColor = inActiveColor;
        return this;
    }

    /**
     * set background color
     *
     * @param backgroundColor res code for the default background color
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setBackgroundColor(int backgroundColor) {
        mBackgroundColor = backgroundColor;
        return this;
    }

    /**
     * set first selected position
     *
     * @param position position of tab that needs to be selected by default
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setFirstSelectedPosition(int position) {
        mPosition = position;
        if (mTabSelectedListener != null) {
            mTabSelectedListener.onTabSelected(position);
        }
        return this;
    }

    /**
     * This method should be called at the end of all customisation method.
     * This method will take all changes in to consideration and redraws tabs.
     */
    public void initialise() {
        if (mMode == MODE_DEFAULT) {
            if (mBottomNavigationItems.size() <= MIN_SIZE) {
                mMode = MODE_FIXED;
            } else {
                mMode = MODE_SHIFTING;
            }
        }

        if (mBackgroundStyle == BACKGROUND_STYLE_DEFAULT) {
            if (mMode == MODE_FIXED) {
                mBackgroundStyle = BACKGROUND_STYLE_STATIC;
            } else {
                mBackgroundStyle = BACKGROUND_STYLE_RIPPLE;
            }
        }
        needInit = true;
        invalidate();
    }

    /**
     * set tab selected listener
     *
     * @param tabSelectedListener callback listener for tabs
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setTabSelectedListener(OnTabSelectedListener tabSelectedListener) {
        this.mTabSelectedListener = tabSelectedListener;
        return this;
    }

    /**
     * ripple animation will be 2.5 times this animation duration.
     *
     * @param animationDuration animation duration for tab animations
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setAnimationDuration(int animationDuration) {
        this.mAnimationDuration = animationDuration;
        this.mRippleAnimationDuration = (int) (animationDuration * 2.5);
        return this;
    }

    /**
     * Clears all stored data and this helps to re-initialise tabs from scratch
     */
    public void clearAll() {
        mBottomNavigationItems.clear();
    }

    /**
     * Should be called only after initialization of BottomBar(i.e after calling initialize method)
     *
     * @param newPosition to select a tab after bottom navigation bar is initialised
     */
    public void selectTab(int newPosition) {
        selectTab(newPosition, true);
    }

    /**
     * Should be called only after initialization of BottomBar(i.e after calling initialize method)
     *
     * @param newPosition  to select a tab after bottom navigation bar is initialised
     * @param callListener should this change call listener callbacks
     */
    public void selectTab(int newPosition, boolean callListener) {
        choose(newPosition, callListener);
    }

    private void choose(int targetPosition, boolean callListener) {
        if (callListener && targetPosition == mPosition && mTabSelectedListener != null) {
            mTabSelectedListener.onTabReselected(mPosition);
        }
        if (targetPosition == mPosition
                || mBottomNavigationItems == null
                || targetPosition >= mBottomNavigationItems.size()) {
            return;
        }
        if (changeAnimatorValue != null && changeAnimatorValue.isRunning()) {
            return;
        }
        if (overAnimatorValue != null && overAnimatorValue.isRunning()) {
            overAnimatorValue.stop();
        }
        if (callListener && mTabSelectedListener != null) {
            mTabSelectedListener.onTabSelected(downPostion);
            mTabSelectedListener.onTabUnselected(mPosition);
        }
        lastPosition = mPosition;
        mPosition = targetPosition;
        changePose = 0;
        overPose = 0;
        if (mBottomNavigationItems.get(mPosition) != null
                && mBottomNavigationItems.get(mPosition).getBadgeItem() != null
                && mBottomNavigationItems.get(mPosition).getBadgeItem().isHideOnSelect()
                && !mBottomNavigationItems.get(mPosition).getBadgeItem().isHidden()) {
            mBottomNavigationItems.get(mPosition).getBadgeItem().hide();
        }
        doAnimator();
    }

    private void doAnimator() {
        if (changeAnimatorValue == null) {
            changeAnimatorValue = new AnimatorValue();
            changeAnimatorValue.setDuration(mAnimationDuration);
            changeAnimatorValue.setCurveType(Animator.CurveType.LINEAR);
            changeAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            changePose = v;
                        }
                    });
        }
        if (overAnimatorValue == null) {
            overAnimatorValue = new AnimatorValue();
            overAnimatorValue.setDuration(mRippleAnimationDuration);
            overAnimatorValue.setCurveType(Animator.CurveType.LINEAR);
            overAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            overPose = v;
                            getContext()
                                    .getUITaskDispatcher()
                                    .asyncDispatch(
                                            new Runnable() {
                                                @Override
                                                public void run() {
                                                    invalidate();
                                                }
                                            });
                        }
                    });
        }

        changeAnimatorValue.start();
        overAnimatorValue.start();
    }

    /**
     * show BottomNavigationBar if it is hidden and hide if it is shown
     */
    public void toggle() {
        toggle(true);
    }

    /**
     * show BottomNavigationBar if it is hidden and hide if it is shown
     *
     * @param animate is animation enabled for toggle
     */
    public void toggle(boolean animate) {
        if (mIsHidden) {
            show(animate);
        } else {
            hide(animate);
        }
    }

    /**
     * show bar
     * show with animation
     */
    public void show() {
        show(true);
    }

    /**
     * show bar
     *
     * @param animate is animation enabled for show
     */
    public void show(boolean animate) {
        if (!mIsHidden) {
            return;
        }
        mIsHidden = false;
        if (showAnimatorValue == null) {
            showAnimatorValue = new AnimatorValue();
        } else if (showAnimatorValue.isRunning()) {
            showAnimatorValue.stop();
        }
        if (animate) {
            int start = showBottom;
            int end = 0;
            showAnimatorValue.setDuration(showDurationTime * (start - end) / (height + mElevation));
            showAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            showBottom = (int) (start + (end - start) * v);
                            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
                            layoutConfig.setMarginBottom(-showBottom);
                            setLayoutConfig(layoutConfig);
                        }
                    });
            showAnimatorValue.start();
        } else {
            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
            layoutConfig.setMarginBottom(0);
            setLayoutConfig(layoutConfig);
        }
    }

    /**
     * hide bar
     * hide with animation
     */
    public void hide() {
        hide(true);
    }

    /**
     * hide bar
     *
     * @param animate is animation enabled for hide
     */
    public void hide(boolean animate) {
        if (mIsHidden) {
            return;
        }
        mIsHidden = true;
        if (showAnimatorValue == null) {
            showAnimatorValue = new AnimatorValue();
        } else if (showAnimatorValue.isRunning()) {
            showAnimatorValue.stop();
        }
        if (animate) {
            int start = showBottom;
            int end = height + mElevation;
            showAnimatorValue.setDuration(showDurationTime * (end - start) / (height + mElevation));
            showAnimatorValue.setValueUpdateListener(
                    new AnimatorValue.ValueUpdateListener() {
                        @Override
                        public void onUpdate(AnimatorValue animatorValue, float v) {
                            showBottom = (int) (start + (end - start) * v);
                            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
                            layoutConfig.setMarginBottom((int) (-showBottom));
                            setLayoutConfig(layoutConfig);
                        }
                    });
            showAnimatorValue.start();
        } else {
            ComponentContainer.LayoutConfig layoutConfig = getLayoutConfig();
            layoutConfig.setMarginBottom(-height);
            setLayoutConfig(layoutConfig);
        }
    }

    public boolean isHidden() {
        return mIsHidden;
    }

    /**
     * get active color
     *
     * @return activeColor
     */
    public int getActiveColor() {
        return mActiveColor;
    }

    /**
     * get in-active color
     *
     * @return in-active color
     */
    public int getInActiveColor() {
        return mInActiveColor;
    }

    /**
     * get background color
     *
     * @return background color
     */
    public int getBackgroundColor() {
        return mBackgroundColor;
    }

    /**
     * get current selected position
     *
     * @return current selected position
     */
    public int getCurrentSelectedPosition() {
        return mPosition;
    }

    /**
     * get animation duration
     *
     * @return animation duration
     */
    public int getAnimationDuration() {
        return mAnimationDuration;
    }

    /**
     * set touch effect.
     *
     * @param touchEffect the touch effect
     * @return this, to allow builder pattern
     */
    public BottomNavigationBar setTouchEffect(boolean touchEffect) {
        this.touchEffect = touchEffect;
        return this;
    }

    /**
     * get touch effect
     *
     * @return touch effect
     */
    public boolean getTouchEffect() {
        return touchEffect;
    }

    private int getCurrentCenterX(int index, int selectIndex) {
        int centerX = 0;
        if (mBottomNavigationItems != null && mBottomNavigationItems.size() > 0) {
            for (int i = 0; i < mBottomNavigationItems.size() && i <= index; i++) {
                if (index == i) {
                    if (selectIndex == i) {
                        centerX += selectWidth / 2;
                    } else {
                        centerX += unSelectWidth / 2;
                    }
                } else {
                    if (selectIndex == i) {
                        centerX += selectWidth;
                    } else {
                        centerX += unSelectWidth;
                    }
                }
            }
        }
        return centerX;
    }

    private int getPostionFromX(float touchX) {
        int x = offsetX;
        if (touchX < x) {
            return -1;
        }
        if (mBottomNavigationItems != null && mBottomNavigationItems.size() > 0) {
            for (int i = 0; i < mBottomNavigationItems.size(); i++) {
                if (mPosition == i) {
                    x += selectWidth;
                } else {
                    x += unSelectWidth;
                }
                if (x >= touchX) {
                    return i;
                }
            }
        }
        return -1;
    }

    private void getMeasurementsForFixedMode(Context context, int screenWidth, int noOfTabs, boolean scrollable) {
        int minWidth = (int) dp2px(104);
        int maxWidth = (int) dp2px(120);

        int itemWidth = screenWidth / noOfTabs;

        if (itemWidth < minWidth && scrollable) {
            itemWidth = (int) dp2px(120);
        } else if (itemWidth > maxWidth) {
            itemWidth = maxWidth;
        }

        unSelectWidth = itemWidth;
        selectWidth = itemWidth;
        offsetX = (getWidth() - itemWidth * noOfTabs) / 2;
    }

    private void getMeasurementsForShiftingMode(Context context, int screenWidth, int noOfTabs, boolean scrollable) {
        int[] result = new int[2];

        int minWidth = (int) dp2px(64);
        int maxWidth = (int) dp2px(96);

        double minPossibleWidth = minWidth * (noOfTabs + 0.5);
        double maxPossibleWidth = maxWidth * (noOfTabs + 0.75);
        int itemWidth;
        int itemActiveWidth;

        if (screenWidth < minPossibleWidth) {
            if (scrollable) {
                itemWidth = minWidth;
                itemActiveWidth = (int) (minWidth * 1.5);
            } else {
                itemWidth = (int) (screenWidth / (noOfTabs + 0.5));
                itemActiveWidth = (int) (itemWidth * 1.5);
            }
        } else if (screenWidth > maxPossibleWidth) {
            itemWidth = maxWidth;
            itemActiveWidth = (int) (itemWidth * 1.75);
        } else {
            double minPossibleWidth1 = minWidth * (noOfTabs + 0.625);
            double minPossibleWidth2 = minWidth * (noOfTabs + 0.75);
            itemWidth = (int) (screenWidth / (noOfTabs + 0.5));
            itemActiveWidth = (int) (itemWidth * 1.5);
            if (screenWidth > minPossibleWidth1) {
                itemWidth = (int) (screenWidth / (noOfTabs + 0.625));
                itemActiveWidth = (int) (itemWidth * 1.625);
                if (screenWidth > minPossibleWidth2) {
                    itemWidth = (int) (screenWidth / (noOfTabs + 0.75));
                    itemActiveWidth = (int) (itemWidth * 1.75);
                }
            }
        }

        unSelectWidth = itemWidth;
        selectWidth = itemActiveWidth;
        offsetX = (getWidth() - (itemActiveWidth + itemWidth * (noOfTabs - 1))) / 2;
    }

    private float getTouchX(TouchEvent touchEvent, int index) {
//        float x = 0;
//        if (touchEvent.getPointerCount() > index) {
//            int[] xy = getLocationOnScreen();
//            if (xy != null && xy.length == 2) {
//                x = touchEvent.getPointerScreenPosition(index).getX() - xy[0];
//            } else {
//                x = touchEvent.getPointerPosition(index).getX();
//            }
//        }
//        return x;
        return touchEvent.getPointerPosition(index).getX();
    }

    private float getTouchY(TouchEvent touchEvent, int index) {
        float y = 0;
        if (touchEvent.getPointerCount() > index) {
            int[] xy = getLocationOnScreen();
            if (xy != null && xy.length == 2) {
                y = touchEvent.getPointerScreenPosition(index).getY() - xy[1];
            } else {
                y = touchEvent.getPointerPosition(index).getY();
            }
        }
        return y;
    }

    private int dp2px(float dp) {
        return (int) (getResourceManager().getDeviceCapability().screenDensity / 160 * dp);
    }

    /**
     * Callback interface invoked when a tab's selection state changes.
     */
    public interface OnTabSelectedListener {
        /**
         * Called when a tab enters the selected state.
         *
         * @param position The position of the tab that was selected
         */
        void onTabSelected(int position);

        /**
         * Called when a tab exits the selected state.
         *
         * @param position The position of the tab that was unselected
         */
        void onTabUnselected(int position);

        /**
         * Called when a tab that is already selected is chosen again by the user. Some applications
         * may use this action to return to the top level of a category.
         *
         * @param position The position of the tab that was reselected.
         */
        void onTabReselected(int position);
    }

    public void setFixedTitleSizeActive(int dp) {
        fixedTitleSizeActive = dp2px(dp);
    }

    public void setFixedTitleSizeInActive(int dp) {
        fixedTitleSizeInActive = dp2px(dp);
    }

    public void setFixedTopActive(int dp) {
        fixedTopActive = dp2px(dp);
    }

    public void setFixedTopInActive(int dp) {
        fixedTopInActive = dp2px(dp);
    }
}
